package com.xyw.code.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.aliyun.oss.internal.OSSUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xyw.code.config.DataScope;
import com.xyw.code.constant.OssFileConstant;
import com.xyw.code.constant.UserConstant;
import com.xyw.code.constant.XConstant;
import com.xyw.code.dto.UserDTO;
import com.xyw.code.dto.UserInfoDTO;
import com.xyw.code.entity.SysDict;
import com.xyw.code.entity.SysUser;
import com.xyw.code.entity.SysUserRole;
import com.xyw.code.exception.BaseException;
import com.xyw.code.mapper.SysUserMapper;
import com.xyw.code.security.XUser;
import com.xyw.code.security.util.JwtUtil;
import com.xyw.code.security.util.SecurityUtil;
import com.xyw.code.service.ISysDeptService;
import com.xyw.code.service.ISysMenuService;
import com.xyw.code.service.ISysUserRoleService;
import com.xyw.code.service.ISysUserService;
import com.xyw.code.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author xuyiwei
 * @since 2019-07-30
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    private Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);

    @Autowired
    private ISysMenuService menuService;

    @Autowired
    private ISysUserRoleService userRoleService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private ISysDeptService deptService;


    @Override
    public SysUser findByUserName(String username) {
        return baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
                .select(SysUser::getId, SysUser::getUsername, SysUser::getDeptId, SysUser::getPassword)
                .eq(SysUser::getUsername, username));
    }

    @Override
    public Set<String> findPermsByUserId(Integer userId) {
        return menuService.findPermsByUserId(userId).stream().
                filter(StringUtils::isNotEmpty).collect(Collectors.toSet());
    }

    @Override
    public Set<String> findRoleIdByUserId(Integer userId) {
        return userRoleService
                .selectUserRoleListByUserId(userId)
                .stream()
                .map(sysUserRole -> "ROLE_" + sysUserRole.getId())
                .collect(Collectors.toSet());
    }

    @Override
    public Set<String> findRoleNameByUserId(Integer userId) {
        return userRoleService
                .selectUserRoleListByUserId(userId)
                .stream()
                .map(sysUserRole -> "ROLE_" + sysUserRole.getId())
                .collect(Collectors.toSet());
    }

    @Override
    public String login(String username, String password, String captcha, String uuid,HttpServletRequest request) {
        // 验证验证码
        // 从redis中获取之前保存的验证码跟前台传来的验证码进行匹配
        Object kaptcha = redisTemplate.opsForValue().get(XConstant.X_IMAGE_SESSION_KEY+"_"+uuid);
        //开发中把验证码去掉
//        if (kaptcha == null) {
//            throw new BaseException("验证码已失效", 402);
//        }
//        if (!captcha.toLowerCase().equals(kaptcha)) {
//            throw new BaseException("验证码错误", 402);
//        }
        //用户验证
        Authentication authentication = null;
        try {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername()去验证用户名和密码，
            // 如果正确，则存储该用户名密码到security 的 context中
            //前端传递的密码解密
            password = PasswordUtil.aesDecrypt(password);
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new BaseException("用户名或密码错误", 402);
            } else if (e instanceof DisabledException) {
                throw new BaseException("账户被禁用", 402);
            } else if (e instanceof AccountExpiredException) {
                throw new BaseException("账户过期无法验证", 402);
            } else {
                throw new BaseException("账户被锁定,无法登录", 402);
            }
        }
        //存储认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //生成token
        XUser userDetail = (XUser) authentication.getPrincipal();
        String token = JwtUtil.generateToken(userDetail);
        log.info("jwt生成的token:"+token);
        XUser user = JwtUtil.getUserFromToken(token);
        log.info("jwtToken得到的用户:"+user);
        return token;
    }

    @Override
    public IPage<SysUser> getUsersWithRolePage(UserDTO userDTO) {
        Page page = new Page(userDTO.getPage(),userDTO.getPageSize());
        if (ObjectUtil.isNotNull(userDTO) && userDTO.getDeptId() != 0){
            userDTO.setDeptList(deptService.selectDeptIds(userDTO.getDeptId()));
        }
        return baseMapper.getUserVosPage(page, userDTO, new DataScope());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean insertUser(UserDTO userDto) {
        if (checkPhone(userDto.getPhone(), null)) {
            throw new BaseException("手机号重复");
        }
        SysUser sysUser = new SysUser();

        BeanUtils.copyProperties(userDto, sysUser);
        // 默认密码 123456
        sysUser.setPassword(XUtil.encode("123456"));
        baseMapper.insert(sysUser);
        List<SysUserRole> userRoles = userDto.getRoleList().stream().map(item -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setRoleId(item);
            sysUserRole.setUserId(sysUser.getId());
            return sysUserRole;
        }).collect(Collectors.toList());
        return userRoleService.saveBatch(userRoles);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateUser(UserDTO userDto) {
        if (checkPhone(userDto.getPhone(), null)) {
            throw new BaseException("手机号重复");
        }
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userDto, sysUser);
        baseMapper.updateById(sysUser);
        userRoleService.remove(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId, sysUser.getId()));
        List<SysUserRole> userRoles = userDto.getRoleList().stream().map(item -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setRoleId(item);
            sysUserRole.setUserId(sysUser.getId());
            return sysUserRole;
        }).collect(Collectors.toList());
        return userRoleService.saveBatch(userRoles);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeUser(Integer userId) {
        SysUser sysUser = new SysUser();
        sysUser.setId(userId);
        sysUser.setStatus(UserConstant.UserStatusEnum.DELETE.getType());
        baseMapper.updateById(sysUser);
        return userRoleService.remove(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId, userId));
    }

    @Override
    public boolean restPass(Integer userId) {
        return baseMapper.updateById(new SysUser().setPassword(new BCryptPasswordEncoder().encode("123456")).setId(userId)) > 0;
    }

    @Override
    public String uploadAvatar(MultipartFile file) {
        return AliyunOSSUtil.upLoad(file, OssFileConstant.avatar);
    }

    @Override
    public Boolean updateUserInfo(UserInfoDTO userDto) {
        XUser user = SecurityUtil.getUser();
        SysUser sysUser = new SysUser();
        sysUser.setId(user.getUserId());
        BeanUtils.copyProperties(userDto,sysUser);
        return updateById(sysUser);
    }

    @Override
    public Boolean updatePass(String oldPass, String newPass) {
        //先验证老密码对不对
        SysUser user = findByUserName(SecurityUtil.getUser().getUsername());
        if(!XUtil.validatePass(oldPass,user.getPassword())){
            throw new BaseException("原密码错误");
        }
        if(oldPass.equals(newPass)){
            throw new BaseException("新老密码不能相同");
        }

        String newPassEncode = XUtil.encode(newPass);
        user.setPassword(newPassEncode);
        return updateById(user);
    }

    @Override
    public Boolean updateEmail(String email, String code,String sessionCode) {
        if(StringUtils.isEmpty(code)){
            throw new BaseException("验证码不能为空");
        }
        if(StringUtils.isEmpty(sessionCode)){
            throw new BaseException("验证码已过期");
        }
        if(!code.toLowerCase().equals(sessionCode.toLowerCase())){
            throw new BaseException("验证码错误");
        }
        SysUser sysUser = findByUserName(SecurityUtil.getUser().getUsername());
        sysUser.setEmail(email);
        return updateById(sysUser);
    }


    /**
     * 判断手机号码是不是重复  如果重复 返回true 如果不重复返回false
     * @param phone
     * @param userId
     * @return
     */
    public boolean checkPhone(String phone,Integer userId){
        LambdaQueryWrapper<SysUser> wrappers = Wrappers.<SysUser>lambdaQuery();
        wrappers.eq(SysUser::getPhone,phone);
        if(userId!=null){
            wrappers.ne(SysUser::getId,userId);
        }
        List<SysUser> list = baseMapper.selectList(wrappers);
        if(list.size()>0){
            return true;
        }
        return false;
    }
}
